/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#include <sxTetgen_TetraFile.h>
#include <sxLineParser.h>
#include <sxScanner.h>
#include <fstream>

Sx::Tetgen::File::Tetgen_TetraFile::Tetgen_TetraFile() {}
Sx::Tetgen::File::Tetgen_TetraFile::~Tetgen_TetraFile() {}

bool Sx::Tetgen::File::Tetgen_TetraFile::load(const String& nodeFile, const String& eleFile, const String& faceFile) {
	if ( !Tetgen_TetraFile::LoadNodes(nodeFile, this->nodes) ) {
		std::cerr << "[Tetgen_TetraFile::load] Error: Could not load Tet nodes from node file: " << nodeFile << std::endl;
		std::cerr << "  No nodes have been loaded." << std::endl;
		return false;
	}

	if ( !Tetgen_TetraFile::LoadElements(eleFile, this->elements) ) {
		std::cerr << "[Tetgen_TetraFile::load] Error: Could not load Tet elements from node file: " << nodeFile << std::endl;
		std::cerr << "  No elements have been loaded." << std::endl;
		return false;
	}

	if ( !Tetgen_TetraFile::LoadFaces(faceFile, this->faces) ) {
		std::cerr << "[Tetgen_TetraFile::load] Error: Could not load Tet faces from node file: " << nodeFile << std::endl;
		std::cerr << "  No faces have been loaded." << std::endl;
		return false;
	}

	return true;
}

bool Sx::Tetgen::File::Tetgen_TetraFile::save(const String& nodeFile, const String& eleFile, const String& faceFile) {
	if ( !Tetgen_TetraFile::SaveNodes(nodeFile, this->nodes) ) {
		std::cerr << "[Tetgen_TetraFile::load] Error: Could not save Tet nodes to node file: " << nodeFile << std::endl;
		std::cerr << "  No nodes have been saved." << std::endl;
		return false;
	}
	
	if ( !Tetgen_TetraFile::SaveElements(eleFile, this->elements) ) {
		std::cerr << "[Tetgen_TetraFile::load] Error: Could not save Tet elements to node file: " << nodeFile << std::endl;
		std::cerr << "  No elements have been saved." << std::endl;
		return false;
	}

	if ( !Tetgen_TetraFile::SaveFaces(faceFile, this->faces) ) {
		std::cerr << "[Tetgen_TetraFile::load] Error: Could not save Tet faces to node file: " << nodeFile << std::endl;
		std::cerr << "  No faces have been saved." << std::endl;
		return false;
	}

	return true;
}

/*
 * <http://tetgen.berlios.de/fformats.node.html>
 * First line: <# of points> <dimension (must be 3)> <# of attributes> <# of boundary markers (0 or 1)>
 * Remaining lines list # of points:
 * <point #> <x> <y> <z> [attributes] [boundary marker]
 * ...
 */
bool Sx::Tetgen::File::Tetgen_TetraFile::LoadNodes(const String& nodeFile, Util::ArrayList<Tetgen_Node>& nodes) {
	if ( nodeFile.length() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:LoadNodes] Error: Tetra_File name of length 0 provided." << std::endl;
		return false;
	}

	Sx::File::LineParser lineParser;
	lineParser.open(nodeFile);

	Util::Scanner scanner(lineParser.nextLine());
	unsigned int nodeCount = scanner.nextInt();
	unsigned int dimension = scanner.nextInt();
	unsigned int attributeCount = scanner.nextInt();
	unsigned int boundaryMarker = scanner.nextInt();

	if ( dimension != 3 ) {
		std::cerr << "[Tetgen_TetraFile::LoadNodes] Error: Cannot load Tet node file with dimension: " << dimension << std::endl;
		std::cerr << "  Tet node dimension must be 3." << std::endl;
		return false;
	}

	if ( nodeCount == 0 ) {
		std::cerr << "[Tetgen_TetraFile::LoadNodes] Error: No node count found in Tet file: " << nodeFile << std::endl;
		return false;
	}

	unsigned int pointNumber;
	double x, y, z;
	bool boundary;

	for ( unsigned int i = 0; i < nodeCount; i++ ) {
		TetAttribute curAttribute;
		Tetgen_Node curNode;

		scanner.setString(lineParser.nextLine());

		//----------------------------------------------------------------------
		// Comment
		//----------------------------------------------------------------------
		if ( scanner.peekChar() == '#' ) continue;

		pointNumber = scanner.nextInt();

		//----------------------------------------------------------------------
		// Node Position
		//----------------------------------------------------------------------
		x = scanner.nextDouble();
		y = scanner.nextDouble();
		z = scanner.nextDouble();
		curNode.data = Eigen::Vector3d(x, y, z);
		
		//----------------------------------------------------------------------
		// Node Attributes
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < attributeCount; x++ ) {
			curAttribute = scanner.nextFloat();
			curNode.attributes.add(curAttribute);
		}

		//----------------------------------------------------------------------
		// Node Boundary
		//----------------------------------------------------------------------
		if ( boundaryMarker == 1 ) {
			boundary = static_cast<bool>(scanner.nextInt());
			curNode.boundary = boundary;
		}

		nodes.add(curNode);
	}

	return true;
}

/*
 * <http://tetgen.berlios.de/fformats.ele.html>
 * First line: <# of tetrahedra> <nodes per tetrahedron> <# of attributes>
 * Remaining lines list of # of tetrahedra:
 * <tetrahedron #> <node> <node> <node> <node> ... [attributes]
 * ...
 */
bool Sx::Tetgen::File::Tetgen_TetraFile::LoadElements(const String& eleFile, Util::ArrayList<Tetgen_Element>& elements) {
	if ( eleFile.length() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:LoadElements] Error: Tetra_File element filename of length 0 provided." << std::endl;
		return false;
	}

	Sx::File::LineParser lineParser;
	lineParser.open(eleFile);

	Util::Scanner scanner(lineParser.nextLine());

	unsigned int tetCount = scanner.nextInt();
	unsigned int nodesPerTet = scanner.nextInt();
	unsigned int attributeCount = scanner.nextInt();

	if ( tetCount == 0 ) {
		std::cerr << "[Tetgen_TetraFile::LoadElements] Error: No tet count found in Tet file: " << eleFile << std::endl;
		return false;
	}

	unsigned int tetNumber, curNodeIndex;

	for ( unsigned int i = 0; i < tetCount; i++ ) {
		Tetgen_Element curElement;
		TetAttribute curAttribute;

		scanner.setString(lineParser.nextLine());

		//----------------------------------------------------------------------
		// Comment
		//----------------------------------------------------------------------
		if ( scanner.peekChar() == '#' ) continue;

		tetNumber = scanner.nextInt();

		//----------------------------------------------------------------------
		// Element Indices
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < nodesPerTet; x++ ) {
			curNodeIndex = scanner.nextInt();
			curElement.indices.add(curNodeIndex);
		}

		//----------------------------------------------------------------------
		// Element Attributes
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < attributeCount; x++ ) {
			curAttribute = scanner.nextFloat();
			curElement.attributes.add(curAttribute);
		}

		elements.add(curElement);
	}

	return true;
}

/*
 * <http://tetgen.berlios.de/fformats.face.html>
 * First line: <# of faces> <boundary marker (0 or 1)>
 * Remaining lines list of # of faces:
 * <face #> <node> <node> <node> [boundary marker]
 * ...
 */
bool Sx::Tetgen::File::Tetgen_TetraFile::LoadFaces(const String& faceFile, Util::ArrayList<Tetgen_Face>& faces) {
	if ( faceFile.length() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:LoadFaces] Error: Tetra_File face filename of length 0 provided." << std::endl;
		return false;
	}

	Sx::File::LineParser lineParser;
	lineParser.open(faceFile);

	Util::Scanner scanner(lineParser.nextLine());

	unsigned int faceCount = scanner.nextInt();
	unsigned int boundaryMarker = scanner.nextInt();

	if ( faceCount == 0 ) {
		std::cerr << "[Tetgen_TetraFile::LoadFaces] Error: No face count found in Tet file: " << faceFile << std::endl;
		return false;
	}

	unsigned int faceDegree = 3;
	unsigned int faceNumber, curFaceIndex;
	bool boundary;

	for ( unsigned int i = 0; i < faceCount; i++ ) {
		Tetgen_Face curFace;
		scanner.setString(lineParser.nextLine());

		//----------------------------------------------------------------------
		// Comment
		//----------------------------------------------------------------------
		if ( scanner.peekChar() == '#' ) continue;

		faceNumber = scanner.nextInt();

		//----------------------------------------------------------------------
		// Face Indices
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < faceDegree; x++ ) {
			curFaceIndex = scanner.nextInt();
			curFace.indices.add(curFaceIndex);
		}

		//----------------------------------------------------------------------
		// Face Boundary
		//----------------------------------------------------------------------
		if ( boundaryMarker == 1 ) {
			boundary = static_cast<bool>(scanner.nextInt());
			curFace.boundary = boundary;
		}

		faces.add(curFace);
	}

	return true;
}

/*
 * <http://tetgen.berlios.de/fformats.node.html>
 * First line: <# of points> <dimension (must be 3)> <# of attributes> <# of boundary markers (0 or 1)>
 * Remaining lines list # of points:
 * <point #> <x> <y> <z> [attributes] [boundary marker]
 * ...
 */
bool Sx::Tetgen::File::Tetgen_TetraFile::SaveNodes(const String& nodeFile, Util::ArrayList<Tetgen_Node>& nodes) {
	if ( nodeFile.length() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:SaveNodes] Error: Tetra_File name of length 0 provided." << std::endl;
		return false;
	}

	if ( nodes.size() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:SaveNodes] Error: No Tet nodes provided." << std::endl;
		return false;
	}

	std::ofstream out(nodeFile.toAscii().constData());
	
	//--------------------------------------------------------------------------
	// Node File Header
	//--------------------------------------------------------------------------
	out << nodes.size() << " " << 3 << " " << nodes[0].attributes.size() << " " << nodes[0].boundary << std::endl;

	//--------------------------------------------------------------------------
	// Nodes
	//--------------------------------------------------------------------------
	for ( unsigned int i = 0; i < nodes.size(); i++ ) {
		out << (i + 1) << " " << nodes[i].data.x() << " " << nodes[i].data.y() << " " << nodes[i].data.z() << " ";

		//----------------------------------------------------------------------
		// Node Attributes
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < nodes[i].attributes.size(); x++ ) {
			out << nodes[i].attributes[x] << " ";
		}

		//----------------------------------------------------------------------
		// Boundary
		//----------------------------------------------------------------------
		if ( nodes[i].boundary == 1 ) out << nodes[i].boundary;
		out << std::endl;
	}

	out.close();
	return true;
}

/*
 * <http://tetgen.berlios.de/fformats.ele.html>
 * First line: <# of tetrahedra> <nodes per tetrahedron> <# of attributes>
 * Remaining lines list of # of tetrahedra:
 * <tetrahedron #> <node> <node> <node> <node> ... [attributes]
 * ...
 */
bool Sx::Tetgen::File::Tetgen_TetraFile::SaveElements(const String& eleFile, Util::ArrayList<Tetgen_Element>& elements) {
	if ( eleFile.length() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:SaveElements] Error: Tetra_File element filename of length 0 provided." << std::endl;
		return false;
	}

	if ( elements.size() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:SaveNodes] Error: No Tet elements provided." << std::endl;
		return false;
	}

	std::ofstream out(eleFile.toAscii().constData());

	//--------------------------------------------------------------------------
	// Element File Header
	//--------------------------------------------------------------------------
	out << elements.size() << " " << elements[0].indices.size() << " " << elements[0].attributes.size() << std::endl;

	//--------------------------------------------------------------------------
	// Elements
	//--------------------------------------------------------------------------
	for ( unsigned int i = 0; i < elements.size(); i++ ) {
		
		//----------------------------------------------------------------------
		// Indices
		//----------------------------------------------------------------------
		out << (i + 1) << " ";
		for ( unsigned int x = 0; x < elements[i].indices.size(); x++ )
			out << elements[i].indices[x] << " ";

		//----------------------------------------------------------------------
		// Attributes
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < elements[i].indices.size(); x++ )
			out << elements[i].attributes[x] << " ";

		out << std::endl;
	}

	out.close();
	return true;
}

/*
 * <http://tetgen.berlios.de/fformats.face.html>
 * First line: <# of faces> <boundary marker (0 or 1)>
 * Remaining lines list of # of faces:
 * <face #> <node> <node> <node> [boundary marker]
 * ...
 */
bool Sx::Tetgen::File::Tetgen_TetraFile::SaveFaces(const String& faceFile, Util::ArrayList<Tetgen_Face>& faces) {
	if ( faceFile.length() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:SaveFaces] Error: Tetra_File face filename of length 0 provided." << std::endl;
		return false;
	}

	if ( faces.size() == 0 ) {
		std::cerr << "[Tetgen_TetraFile:SaveNodes] Error: No Tet faces provided." << std::endl;
		return false;
	}

	std::ofstream out(faceFile.toAscii().constData());

	//--------------------------------------------------------------------------
	// Face File Header
	//--------------------------------------------------------------------------
	out << faces.size() << " " << faces[0].boundary << std::endl;

	for ( unsigned int i = 0; i < faces.size(); i++ ) {
		//----------------------------------------------------------------------
		// Indices
		//----------------------------------------------------------------------
		out << (i + 1) << " ";
		for ( unsigned int x = 0; x < faces[i].indices.size(); x++ ) {
			out << faces[i].indices[x] << " ";
		}

		//----------------------------------------------------------------------
		// Boundary
		//----------------------------------------------------------------------
		if ( faces[i].boundary == 1 ) out << faces[i].boundary;

		out << std::endl;
	}

	out.close();
	return true;
}

Sx::Util::ArrayList<Sx::Tetgen::File::Tetgen_Node>& Sx::Tetgen::File::Tetgen_TetraFile::getNodes() {
	return this->nodes;
}

Sx::Util::ArrayList<Sx::Tetgen::File::Tetgen_Element>& Sx::Tetgen::File::Tetgen_TetraFile::getElements() {
	return this->elements;
}

Sx::Util::ArrayList<Sx::Tetgen::File::Tetgen_Face>& Sx::Tetgen::File::Tetgen_TetraFile::getFaces() {
	return this->faces;
}
